package com.wontube.core.dao;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.wontube.core.conf.Database;
import com.wontube.core.conf.Datasource;
import com.wontube.core.pub.AppEnvironment;
import com.wontube.core.pub.ConnectionPoolManager;
import com.wontube.core.pub.CustomerDatasource;


/**
 * @author yuechao
 * 
 * 
 */
public final class SqlSessionManager {
	private SqlSessionManager() {
	}

	/**
	 * 单一实例
	 * 
	 */
	public static final SqlSessionManager manager = new SqlSessionManager();

	// 日志
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	// 静态数据源配置
	private final HashMap<String, SqlSessionFactory> mapStatic = new HashMap<String, SqlSessionFactory>();

	// 动态数据源配置
	private final HashMap<String, SessionWrapper> mapDynamic = new HashMap<String, SessionWrapper>();

	public void initializeStaticDatasource(Datasource ds) throws IOException {
		Resources.setCharset(Charset.forName(AppEnvironment.charsetName));
		
		Properties props = new Properties();
		for (Database db : ds.getDatabases()) {
			props.setProperty("db.driver", db.getDriver());
			props.setProperty("db.url", db.getUrl());
			props.setProperty("db.account", db.getAccount());
			props.setProperty("db.password", db.getPassword());
			String resource = AppEnvironment.daoConfigPath;			
			Reader reader = Resources.getResourceAsReader(resource);

			try {
				SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
				this.mapStatic.put(db.getName(), factory);
				logger.info("{}: {}  database initialized", db.getName(), db.getUrl());
			} finally {
				reader.close();
			}
		}
	}

	/**
	 * 初始化动态数据源
	 * 
	 * @throws SQLException
	 * @throws IOException
	 */
	public void initDynamicDatasource(List<CustomerDatasource> list) throws IOException {
		String resource = AppEnvironment.customerConfigPath;
		Reader reader = Resources.getResourceAsReader(resource);
		try {
			SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
			logger.info("customer mapper initialized");

			for (CustomerDatasource ds : list) {
				registerCustomerDatasource(factory, ds);
			}
		} finally {
			reader.close();
		}
	}

	/**
	 * 注册一个客户数据源
	 * 
	 * @param ds
	 * @throws IOException
	 */
	public void registerCustomerDatasource(SqlSessionFactory factory, CustomerDatasource ds) throws IOException {
		ConnectionPoolManager manager = new ConnectionPoolManager(ds.createConnectionPoolDataSource(),
				ds.getMaxConnectionCount());
		SessionWrapper wrapper = new SessionWrapper(manager, factory);

		this.mapDynamic.put(ds.getDatasourceId(), wrapper);
		logger.info("{} database initialized", ds.getDatabaseName());
	}

	/**
	 * 移除一个客户数据源
	 * 
	 * @param ds_id
	 *            数据源ID
	 */
	public void removeCustomerDatasource(String dsId) {
		this.mapDynamic.remove(dsId);
	}

	/**
	 * 是否包含指定名称的数据源
	 * 
	 * @param name
	 * @return
	 */
	public boolean containsDatasource(String name) {
		if (this.mapDynamic.containsKey(name)) {
			return true;
		}

		return this.mapStatic.containsKey(name);
	}

	/**
	 * 获取指定名称的数据源
	 * 
	 * @param name
	 * @return
	 * @throws SQLException
	 */
	public SqlSession get(String name, boolean autoCommit) throws RuntimeException {
		SqlSessionFactory factory = this.mapStatic.get(name);
		if (factory != null) {
			return factory.openSession(autoCommit);
		}

		SessionWrapper wrapper = this.mapDynamic.get(name);
		if (manager != null) {
			try {
				Connection conn = wrapper.getPoolManager().getConnection();
				conn.setAutoCommit(autoCommit);
				return wrapper.getSessionFactory().openSession(conn);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}

		throw new RuntimeException("datasource " + name + " cannot be found!");
	}
	
	/**
	 * 得到用于批处理的sqlsession
	 * @param name
	 * @return
	 */
	public SqlSession getBatch(String name){
		SqlSessionFactory factory = this.mapStatic.get(name);
		if (factory != null) {
			//set autocomit = false
			return factory.openSession(ExecutorType.BATCH,  false);
		}
		
		throw new RuntimeException("datasource " + name + " cannot be found!");
	}
}
